package cn.xuan.scan.dao.query.param;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.collection.internal.PersistentSet;
import org.hibernate.internal.SessionImpl;
import org.hibernate.proxy.HibernateProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;

public class BundingChildren {
	private Object parentInst;
	private HashMap parentChildrenClsMap;
	private HashMap clsInstMap;
	private Class rootClass;
	private QueryTree queryTree;
	private HashMap sameClassMap;
	private Session session;
	private static final Logger logger = LoggerFactory.getLogger(BundingChildren.class);

	public BundingChildren(QueryTree queryTree, Object parentInst, Session session) {
		this.session = session;
		this.queryTree = queryTree;
		if (queryTree != null) {
			this.parentChildrenClsMap = queryTree.getParenChildrenMap();
			this.parentInst = parentInst;
			if (queryTree.getRootClass() != null) {
				this.rootClass = queryTree.getRootClass();
			} else {
				this.rootClass = parentInst.getClass();
			}

			this.clsInstMap = new HashMap();
			Vector objects = new Vector();
			objects.add(parentInst);
			this.clsInstMap.put(this.rootClass, objects);
		}
	}

	public void getChildrenInst() {
		if (this.queryTree != null) {
			this.sameClassMap = new HashMap();
			HashSet childrenCls = this.getChildClassNamesFromMap(this.rootClass);
			if (childrenCls != null) {
				Iterator it = childrenCls.iterator();

				while (it.hasNext()) {
					String childClassName = (String) it.next();
					this.getChildrenInstOfClass(this.rootClass, childClassName, 0, 0);
				}

			}
		}
	}

	private HashSet getChildClassNamesFromMap(Class parentClass) {
		if (parentClass == null) {
			return null;
		} else {
			HashSet childrenCls = null;
			String className = ClassUtils.getShortName(parentClass);
			childrenCls = (HashSet) this.parentChildrenClsMap.get(className);
			if (childrenCls == null && parentClass.getSuperclass() != null) {
				childrenCls = this.getChildClassNamesFromMap(parentClass.getSuperclass());
			}

			return childrenCls;
		}
	}

	private void getChildrenInstOfClass(Class parentCls, String childClsName, int doRoundTimes, int doSameTimes) {
		int initRoundTimes = doRoundTimes;
		int initSameTimes = doSameTimes;
		Vector vInst = (Vector) this.clsInstMap.get(parentCls);
		if (vInst != null && vInst.size() != 0) {
			Vector vObjectToDeal = new Vector();

			for (int i = 0; i < vInst.size(); ++i) {
				Object inst = vInst.get(i);
				if (inst instanceof Object[]) {
					Object[] objs = (Object[]) inst;

					for (int j = 0; j < objs.length; ++j) {
						Object obj = objs[j];
						if (obj.getClass() == parentCls) {
							vObjectToDeal.add(obj);
						}
					}
				} else {
					vObjectToDeal.add(inst);
				}
			}

			Class childCls = null;

			for (int i = 0; i < vObjectToDeal.size(); ++i) {
				Object inst = vObjectToDeal.get(i);
				childCls = this.getChildrenInst(inst, childClsName);
				if (childCls != null) {
					HashSet decendentCls = (HashSet) this.parentChildrenClsMap.get(childClsName);
					if (decendentCls != null && !decendentCls.isEmpty()) {
						String tempDecendentClsName;
						for (Iterator itChild = decendentCls.iterator(); itChild.hasNext(); this
								.getChildrenInstOfClass(childCls, tempDecendentClsName, doRoundTimes, doSameTimes)) {
							tempDecendentClsName = (String) itChild.next();
							if (tempDecendentClsName.equals(ClassUtils.getShortName(parentCls))
									&& !parentCls.equals(childCls)) {
								if (doRoundTimes > 0) {
									boolean doRoundTimes1 = false;
									break;
								}

								++doRoundTimes;
							}

							if (tempDecendentClsName.equals(ClassUtils.getShortName(childCls))) {
								if (parentCls.equals(childCls)) {
									return;
								}

								if (doSameTimes > 0) {
									boolean doSameTimes1 = false;
									break;
								}

								++doSameTimes;
							}
						}

						doRoundTimes = initRoundTimes;
						doSameTimes = initSameTimes;
					}
				}
			}

		}
	}

	private Class getChildrenInst(Object objParent, String methodName) {
		if (objParent != null && methodName != null && !methodName.equals("")) {
			Class fatherCls = objParent.getClass();
			String getMethodName = null;
			Method getMethod = null;
			new Vector();
//			int exceptionNum = false;
			Object obj = null;
			Class childCls = null;
			Vector vtobjs = this.getObjectsWithMethodName(objParent, methodName);
			if (vtobjs != null && vtobjs.size() > 0) {
				childCls = vtobjs.get(0).getClass();
				if (objParent.getClass().getName().equals(childCls.getName())) {
					for (int i = 0; i < vtobjs.size(); ++i) {
						Object subObj = vtobjs.get(i);
						this.getSameChildrenInst(subObj, methodName);
					}
				} else if (this.clsInstMap.get(childCls) == null) {
					this.clsInstMap.put(childCls, new Vector(vtobjs));
				} else {
					vtobjs.removeAll((Vector) this.clsInstMap.get(childCls));
					((Vector) this.clsInstMap.get(childCls)).addAll(vtobjs);
				}
			}

			return childCls;
		} else {
			return null;
		}
	}

	private Vector invokeMethodWithName(Object parentObj, String methodName) {
		Vector vReturn = new Vector();
		Class fatherCls = parentObj.getClass();

		try {
			Method getMethod = fatherCls.getMethod(methodName);
			Object obj = getMethod.invoke(parentObj);
			if (obj != null) {
				if (obj instanceof PersistentSet) {
					PersistentSet persistentSet = (PersistentSet) obj;
					if (!Hibernate.isInitialized(persistentSet)) {
						Hibernate.initialize(persistentSet);
					}

					if (persistentSet.size() > 0) {
						vReturn.addAll(persistentSet);
					}
				} else {
					if (!Hibernate.isInitialized(obj)) {
						if (((HibernateProxy) obj).getHibernateLazyInitializer().getSession() == null
								&& this.session != null) {
							((HibernateProxy) obj).getHibernateLazyInitializer().setSession((SessionImpl) this.session);
						}

						Hibernate.initialize(obj);
					}

					obj = this.handleProxy(parentObj, methodName, fatherCls, obj);
					vReturn.add(obj);
				}
			}
		} catch (Exception var9) {
			;
		}

		return vReturn;
	}

	private Object setObject(Object parentObj, String methodName, Class fatherCls, Object obj) throws Exception {
		try {
			String setMethodName = "s" + methodName.substring(1);
			Class clazz = obj.getClass();
			Method setMethod = null;

			do {
				try {
					setMethod = fatherCls.getMethod(setMethodName, clazz);
				} catch (Exception var9) {
					clazz = clazz.getSuperclass();
				}
			} while (setMethod == null && this.excludeClass(clazz));

			setMethod.invoke(parentObj, obj);
		} catch (Exception var10) {
			;
		}

		return obj;
	}

	private Object handleProxy(Object parentObj, String methodName, Class fatherCls, Object obj) throws Exception {
		if (this.checkProxy(obj)) {
			obj = ((HibernateProxy) obj).getHibernateLazyInitializer().getImplementation();
			this.setObject(parentObj, methodName, fatherCls, obj);
		}

		return obj;
	}

	private boolean excludeClass(Class clazz) {
		return clazz != null && !clazz.getName().startsWith("java.") && !clazz.getName().startsWith("javax.");
	}

	private boolean checkProxy(Object object) {
		return object.getClass().getName().lastIndexOf("_$$_javassist_") != -1;
	}

	private Vector getObjectsWithMethodName(Object parentObj, String methodName) {
		Vector vReturn = new Vector();
		String getMethodName = "get" + methodName + "s";
		vReturn.addAll(this.invokeMethodWithName(parentObj, getMethodName));
		getMethodName = "get" + methodName;
		Vector vObject = this.invokeMethodWithName(parentObj, getMethodName);
		if (vObject.size() > 0) {
			if (vReturn.size() > 0) {
				Object objReturn = vReturn.get(0);
				Object obj = vObject.get(0);
				if (objReturn.getClass().getName().equals(obj.getClass().getName())) {
					vReturn.addAll(vObject);
				}
			} else {
				vReturn.addAll(vObject);
			}
		}

		vReturn.size();
		return vReturn;
	}

	private void putSameObjectToCache(Object obj, String className) {
		Vector vObjInCache = (Vector) this.sameClassMap.get(className);
		if (vObjInCache == null) {
			vObjInCache = new Vector();
		}

		if (!vObjInCache.contains(obj)) {
			vObjInCache.add(obj);
		}

		this.sameClassMap.put(className, vObjInCache);
		Class childCls = obj.getClass();
		Vector vInst = (Vector) this.clsInstMap.get(childCls);
		if (vInst == null) {
			vInst = new Vector();
		}

		if (!vInst.contains(obj)) {
			vInst.add(obj);
		}

		this.clsInstMap.put(childCls, vInst);
	}

	private boolean isSameObjectInCache(Object obj, String className) {
		Vector vObjInCache = (Vector) this.sameClassMap.get(className);
		if (vObjInCache == null) {
			vObjInCache = new Vector();
		}

		return vObjInCache.contains(obj);
	}

	private void getSameChildrenInst(Object obj, String methodName) {
		if (obj != null) {
			String className = obj.getClass().getName();
			this.putSameObjectToCache(obj, className);
			Vector vObjQuery = this.getObjectsWithMethodName(obj, methodName);
			if (vObjQuery != null && vObjQuery.size() != 0) {
				for (int i = 0; i < vObjQuery.size(); ++i) {
					Object objQuery = vObjQuery.get(i);
					if (objQuery.getClass().getName().equals(className)) {
						if (!this.isSameObjectInCache(objQuery, className)) {
							this.getSameChildrenInst(objQuery, methodName);
						} else {
							this.putSameObjectToCache(objQuery, className);
						}
					}
				}

			}
		}
	}
}
